/**
 * Package: svgedit.path
 *
 * Licensed under the Apache License, Version 2
 *
 * Copyright(c) 2011 Alexis Deveria
 * Copyright(c) 2011 Jeff Schiller
 */

// Dependencies:
// 1) jQuery
// 2) browser.js
// 3) math.js
// 4) svgutils.js

var svgedit = svgedit || {};

(function() {

if (!svgedit.path) {
  svgedit.path = {};
}

var svgns = "http://www.w3.org/2000/svg";

var segData = {
  2: ['x','y'],
  4: ['x','y'],
  6: ['x','y','x1','y1','x2','y2'],
  8: ['x','y','x1','y1'],
  10: ['x','y','r1','r2','angle','largeArcFlag','sweepFlag'],
  12: ['x'],
  14: ['y'],
  16: ['x','y','x2','y2'],
  18: ['x','y']
};

var pathFuncs = [];

var link_control_pts = false;

// Stores references to paths via IDs.
// TODO: Make this cross-document happy.
var pathData = {};

svgedit.path.setLinkControlPoints = function(lcp) {
  link_control_pts = lcp;
};

svgedit.path.path = null;

var editorContext_ = null;

svgedit.path.init = function(editorContext) {
  editorContext_ = editorContext;
  
  pathFuncs = [0,'ClosePath'];
  var pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc',
    'LinetoHorizontal', 'LinetoVertical','CurvetoCubicSmooth','CurvetoQuadraticSmooth'];
  $.each(pathFuncsStrs, function(i,s) {
    pathFuncs.push(s+'Abs');
    pathFuncs.push(s+'Rel');
  });
};

svgedit.path.insertItemBefore = function(elem, newseg, index) {
  var list = elem.pathSegList;
  list.insertItemBefore(newseg, index);
};

// TODO: See if this should just live in replacePathSeg
svgedit.path.ptObjToArr = function(type, seg_item) {
  var arr = segData[type], len = arr.length;
  var out = Array(len);
  for(var i=0; i<len; i++) {
    out[i] = seg_item[arr[i]];
  }
  return out;
};

svgedit.path.getGripPt = function(seg, alt_pt) {
  var out = {
    x: alt_pt? alt_pt.x : seg.item.x,
    y: alt_pt? alt_pt.y : seg.item.y
  }, path = seg.path;

  if(path.matrix) {
    var pt = svgedit.math.transformPoint(out.x, out.y, path.matrix);
    out = pt;
  }

  out.x *= editorContext_.getCurrentZoom();
  out.y *= editorContext_.getCurrentZoom();

  return out;
};

svgedit.path.getPointFromGrip = function(pt, path) {
  var out = {
    x: pt.x,
    y: pt.y
  }

  if(path.matrix) {
    var pt = svgedit.math.transformPoint(out.x, out.y, path.imatrix);
    out.x = pt.x;
    out.y = pt.y;
  }

  out.x /= editorContext_.getCurrentZoom();
  out.y /= editorContext_.getCurrentZoom();

  return out;
};

svgedit.path.addPointGrip = function(index, x, y) {
  // create the container of all the point grips
  var pointGripContainer = svgedit.path.getGripContainer();

  var pointGrip = svgedit.utilities.getElem("pathpointgrip_"+index);
  // create it
  if (!pointGrip) {
    pointGrip = document.createElementNS(svgns, "rect");
    svgedit.utilities.assignAttributes(pointGrip, {
      'id': "pathpointgrip_" + index,
      'display': "none",
      'width': svgedit.browser.isTouch() ? 30 : 5,
      'height': svgedit.browser.isTouch() ? 30 : 5,
      'fill': "#fff",
      'stroke': "#4F80FF",
      'shape-rendering': "crispEdges",
      'stroke-width': 1,
      'cursor': 'move',
      'style': 'pointer-events:all',
      'xlink:title': "Drag node to move it. Double-click node to change segment type"
    });
    pointGrip = pointGripContainer.appendChild(pointGrip);

    var grip = $('#pathpointgrip_'+index);
    grip.dblclick(function() {
      if(svgedit.path.path) svgedit.path.path.setSegType();
    });
  }
  if(x && y) {
    // set up the point grip element and display it
    svgedit.utilities.assignAttributes(pointGrip, {
      'x': x-(svgedit.browser.isTouch() ? 15 : 2.5),
      'y': y-(svgedit.browser.isTouch() ? 15 : 2.5),
      'display': "inline"
    });
  }
  return pointGrip;
};

svgedit.path.getGripContainer = function() {
  var c = svgedit.utilities.getElem("pathpointgrip_container");
  if (!c) {
    var parent = svgedit.utilities.getElem("selectorParentGroup");
    c = parent.appendChild(document.createElementNS(svgns, "g"));
    c.id = "pathpointgrip_container";
  }
  return c;
};

svgedit.path.addCtrlGrip = function(id) {
  var pointGrip = svgedit.utilities.getElem("ctrlpointgrip_"+id);
  if(pointGrip) return pointGrip;
    
  pointGrip = document.createElementNS(svgns, "circle");
  svgedit.utilities.assignAttributes(pointGrip, {
    'id': "ctrlpointgrip_" + id,
    'display': "none",
    'r': svgedit.browser.isTouch() ? 15 : 3,
    'fill': "#4F80FF",
    'stroke': '#4F80FF',
    'stroke-opacity': 0,
    'stroke-width': '3',
    'cursor': 'move',
    'style': 'pointer-events:all',
    'xlink:title': "Drag control point to adjust curve properties"
  });
  svgedit.path.getGripContainer().appendChild(pointGrip);
  return pointGrip;
};

svgedit.path.getCtrlLine = function(id) {
  var ctrlLine = svgedit.utilities.getElem("ctrlLine_"+id);
  if(ctrlLine) return ctrlLine;

  ctrlLine = document.createElementNS(svgns, "line");
  svgedit.utilities.assignAttributes(ctrlLine, {
    'id': "ctrlLine_"+id,
    'stroke': "#4F80FF",
    'stroke-width': 1,
    "style": "pointer-events:none"
  });
  svgedit.path.getGripContainer().appendChild(ctrlLine);
  return ctrlLine;
};

svgedit.path.getPointGrip = function(seg, update) {
  var index = seg.index;
  var pointGrip = svgedit.path.addPointGrip(index);
  if(update) {
    var pt = svgedit.path.getGripPt(seg);
    svgedit.utilities.assignAttributes(pointGrip, {
      'x': pt.x-(svgedit.browser.isTouch() ? 15 : 2.5),
      'y': pt.y-(svgedit.browser.isTouch() ? 15 : 2.5),
      'display': "inline"
    });
  }

  return pointGrip;
};

svgedit.path.getControlPoints = function(seg) {
  var item = seg.item;
  var index = seg.index;
  if(!item || !("x1" in item) || !("x2" in item)) return null;
  var cpt = {};     
  var pointGripContainer = svgedit.path.getGripContainer();

  // Note that this is intentionally not seg.prev.item
  var prev = svgedit.path.path.segs[index-1].item;

  var seg_items = [prev, item];

  for(var i=1; i<3; i++) {
    var id = index + 'c' + i;

    var ctrlLine = cpt['c' + i + '_line'] = svgedit.path.getCtrlLine(id);

    var pt = svgedit.path.getGripPt(seg, {x:item['x' + i], y:item['y' + i]});
    var gpt = svgedit.path.getGripPt(seg, {x:seg_items[i-1].x, y:seg_items[i-1].y});

    svgedit.utilities.assignAttributes(ctrlLine, {
      'x1': pt.x,
      'y1': pt.y,
      'x2': gpt.x,
      'y2': gpt.y,
      'display': "inline"
    });

    cpt['c' + i + '_line'] = ctrlLine;

    // create it
    pointGrip = cpt['c' + i] = svgedit.path.addCtrlGrip(id);
    svgedit.utilities.assignAttributes(pointGrip, {
      'cx': pt.x,
      'cy': pt.y,
      'display': "inline"
    });
    cpt['c' + i] = pointGrip;
  }
  return cpt;
};

// This replaces the segment at the given index. Type is given as number.
svgedit.path.replacePathSeg = function(type, index, pts, elem) {
  var path = elem || svgedit.path.path.elem;
  var func = 'createSVGPathSeg' + pathFuncs[type];
  var seg = path[func].apply(path, pts);
  path.pathSegList.replaceItem(seg, index);
};

svgedit.path.getSegSelector = function(seg, update) {
  var index = seg.index;
  var segLine = svgedit.utilities.getElem("segline_" + index);
  if(!segLine) {
    var pointGripContainer = svgedit.path.getGripContainer();
    // create segline
    segLine = document.createElementNS(svgns, "path");
    svgedit.utilities.assignAttributes(segLine, {
      'id': "segline_" + index,
      'display': 'none',
      'fill': "none",
      'stroke': "#0ff",
      'stroke-opacity': 1,
      "shape-rendering": "crispEdges",
      'stroke-width': 2,
      'style':'pointer-events:none',
      'd': 'M0,0 0,0'
    });
    pointGripContainer.appendChild(segLine);
  } 

  if(update) {
    var prev = seg.prev;
    if(!prev) {
      segLine.setAttribute("display", "none");
      return segLine;
    }

    var pt = svgedit.path.getGripPt(prev);
    // Set start point
    svgedit.path.replacePathSeg(2, 0, [pt.x, pt.y], segLine);

    var pts = svgedit.path.ptObjToArr(seg.type, seg.item, true);
    for(var i=0; i < pts.length; i+=2) {
      var pt = svgedit.path.getGripPt(seg, {x:pts[i], y:pts[i+1]});
      pts[i] = pt.x;
      pts[i+1] = pt.y;
    }

    svgedit.path.replacePathSeg(seg.type, 1, pts, segLine);
  }
  return segLine;
};

// Function: smoothControlPoints
// Takes three points and creates a smoother line based on them
// 
// Parameters: 
// ct1 - Object with x and y values (first control point)
// ct2 - Object with x and y values (second control point)
// pt - Object with x and y values (third point)
//
// Returns: 
// Array of two "smoothed" point objects
svgedit.path.smoothControlPoints = this.smoothControlPoints = function(ct1, ct2, pt) {
  // each point must not be the origin
  var x1 = ct1.x - pt.x,
    y1 = ct1.y - pt.y,
    x2 = ct2.x - pt.x,
    y2 = ct2.y - pt.y;
    
  if ( (x1 != 0 || y1 != 0) && (x2 != 0 || y2 != 0) ) {
    var anglea = Math.atan2(y1,x1),
      angleb = Math.atan2(y2,x2),
      r1 = Math.sqrt(x1*x1+y1*y1),
      r2 = Math.sqrt(x2*x2+y2*y2),
      nct1 = editorContext_.getSVGRoot().createSVGPoint(),
      nct2 = editorContext_.getSVGRoot().createSVGPoint();        
    if (anglea < 0) { anglea += 2*Math.PI; }
    if (angleb < 0) { angleb += 2*Math.PI; }
    
    var angleBetween = Math.abs(anglea - angleb),
      angleDiff = Math.abs(Math.PI - angleBetween)/2;
    
    var new_anglea, new_angleb;
    if (anglea - angleb > 0) {
      new_anglea = angleBetween < Math.PI ? (anglea + angleDiff) : (anglea - angleDiff);
      new_angleb = angleBetween < Math.PI ? (angleb - angleDiff) : (angleb + angleDiff);
    }
    else {
      new_anglea = angleBetween < Math.PI ? (anglea - angleDiff) : (anglea + angleDiff);
      new_angleb = angleBetween < Math.PI ? (angleb + angleDiff) : (angleb - angleDiff);
    }
    
    // rotate the points
    nct1.x = r1 * Math.cos(new_anglea) + pt.x;
    nct1.y = r1 * Math.sin(new_anglea) + pt.y;
    nct2.x = r2 * Math.cos(new_angleb) + pt.x;
    nct2.y = r2 * Math.sin(new_angleb) + pt.y;
    
    return [nct1, nct2];
  }
  return undefined;
};

svgedit.path.Segment = function(index, item) {
  this.selected = false;
  this.index = index;
  this.item = item;
  this.type = item.pathSegType;
  
  this.ctrlpts = [];
  this.ptgrip = null;
  this.segsel = null;
};

svgedit.path.Segment.prototype.showCtrlPts = function(y) {
  for (var i in this.ctrlpts) {
    this.ctrlpts[i].setAttribute("display", y ? "inline" : "none");
  }
};

svgedit.path.Segment.prototype.selectCtrls = function(y) {
  $('#ctrlpointgrip_' + this.index + 'c1, #ctrlpointgrip_' + this.index + 'c2').
    attr('fill', '#4F80FF');
};

svgedit.path.Segment.prototype.show = function(y) {
  if(this.ptgrip) {
    this.ptgrip.setAttribute("display", y ? "inline" : "none");
    this.segsel.setAttribute("display", y ? "inline" : "none");
    // Show/hide all control points if available
    this.showCtrlPts(y);
  }
};

svgedit.path.Segment.prototype.select = function(y) {
  if(this.ptgrip) {
    this.ptgrip.setAttribute("stroke", y ? "#4F80FF" : "#4F80FF");
    this.ptgrip.setAttribute("fill", y ? "#4F80FF" : "#fff");
    this.segsel.setAttribute("display", y ? "inline" : "none");
    if(this.ctrlpts) {
      this.selectCtrls(y);
    }
    this.selected = y;
  }
};

svgedit.path.Segment.prototype.addGrip = function() {
  this.ptgrip = svgedit.path.getPointGrip(this, true);
  this.ctrlpts = svgedit.path.getControlPoints(this, true);
  this.segsel = svgedit.path.getSegSelector(this, true);
};

svgedit.path.Segment.prototype.update = function(full) {
  if(this.ptgrip) {
    var pt = svgedit.path.getGripPt(this);
    var reposition = (svgedit.browser.isTouch() ? 15 : 2.5)
    var properties = (this.ptgrip.nodeName === "rect") ? {'x': pt.x-reposition, 'y': pt.y-reposition} : {'cx': pt.x, 'cy': pt.y};
    svgedit.utilities.assignAttributes(this.ptgrip, properties);
    svgedit.path.getSegSelector(this, true);

    if(this.ctrlpts) {
      if(full) {
        this.item = svgedit.path.path.elem.pathSegList.getItem(this.index);
        this.type = this.item.pathSegType;
      }
      svgedit.path.getControlPoints(this);
    } 
    // this.segsel.setAttribute("display", y?"inline":"none");
  }
};

svgedit.path.Segment.prototype.move = function(dx, dy) {
  var item = this.item;
  // fix for path tool dom breakage, amending item does bad things now, so we take a copy and use that. Can probably improve to just take a shallow copy of object
  var cloneItem = item;
  var cur_pts = (this.ctrlpts) 
    ? [cloneItem.x += dx,  cloneItem.y += dy, 
       cloneItem.x1,       cloneItem.y1, 
       cloneItem.x2 += dx, cloneItem.y2 += dy]
    : [cloneItem.x += dx, cloneItem.y += dy];
  
  svgedit.path.replacePathSeg(this.type, this.index, cur_pts);

  if(this.next && this.next.ctrlpts) {
    var next = this.next.item;
    var next_pts = [next.x, next.y, 
      next.x1 += dx, next.y1 += dy, next.x2, next.y2];
    svgedit.path.replacePathSeg(this.next.type, this.next.index, next_pts);
  }

  if(this.mate) {
    // The last point of a closed subpath has a "mate",
    // which is the "M" segment of the subpath
    var item = this.mate.item;
    var pts = [item.x += dx, item.y += dy];
    svgedit.path.replacePathSeg(this.mate.type, this.mate.index, pts);
    // Has no grip, so does not need "updating"?
  }

  this.update(true);
  if(this.next) this.next.update(true);
};

svgedit.path.Segment.prototype.setLinked = function(num) {
  var seg, anum, pt;
  if (num === 2) {
    anum = 1;
    seg = this.next;
    if(!seg) return;
    pt = this.item;
  } else {
    anum = 2;
    seg = this.prev;
    if(!seg) return;
    pt = seg.item;
  }

  var item = seg.item;
  var cloneItem = $.extend({}, item);
  cloneItem['x' + anum ] = pt.x + (pt.x - this.item['x' + num]);
  cloneItem['y' + anum ] = pt.y + (pt.y - this.item['y' + num]);

  var pts = [ 
              cloneItem.x, cloneItem.y,
              cloneItem.x1, cloneItem.y1,
              cloneItem.x2, cloneItem.y2
            ];

  svgedit.path.replacePathSeg(seg.type, seg.index, pts);
  seg.update(true);
};

svgedit.path.Segment.prototype.moveCtrl = function(num, dx, dy) {
  var item = $.extend({}, this.item);

  item['x' + num] += dx;
  item['y' + num] += dy;

  var pts = [item.x,item.y,
    item.x1,item.y1, item.x2,item.y2];
    
  svgedit.path.replacePathSeg(this.type, this.index, pts);
  this.update(true);
};

svgedit.path.Segment.prototype.setType = function(new_type, pts) {
  svgedit.path.replacePathSeg(new_type, this.index, pts);
  this.type = new_type;
  this.item = svgedit.path.path.elem.pathSegList.getItem(this.index);
  this.showCtrlPts(new_type === 6);
  this.ctrlpts = svgedit.path.getControlPoints(this);
  this.update(true);
};

svgedit.path.Path = function(elem) {
  if(!elem || elem.tagName !== "path") {
    throw "svgedit.path.Path constructed without a <path> element";
  }

  this.elem = elem;
  this.segs = [];
  this.selected_pts = [];
  svgedit.path.path = this;

  this.init();
};

// Reset path data
svgedit.path.Path.prototype.init = function() {
  // Hide all grips, etc
  $(svgedit.path.getGripContainer()).find("*").attr("display", "none");
  var segList = this.elem.pathSegList;
  var len = segList.numberOfItems;
  this.segs = [];
  this.selected_pts = [];
  this.first_seg = null;
  
  // Set up segs array
  for(var i=0; i < len; i++) {
    var item = segList.getItem(i);
    var segment = new svgedit.path.Segment(i, item);
    segment.path = this;
    this.segs.push(segment);
  } 
  
  var segs = this.segs;
  var start_i = null;

  for(var i=0; i < len; i++) {
    var seg = segs[i]; 
    var next_seg = (i+1) >= len ? null : segs[i+1];
    var prev_seg = (i-1) < 0 ? null : segs[i-1];
    
    if(seg.type === 2) {
      if(prev_seg && prev_seg.type !== 1) {
        // New sub-path, last one is open,
        // so add a grip to last sub-path's first point
        var start_seg = segs[start_i];
        start_seg.next = segs[start_i+1];
        start_seg.next.prev = start_seg;
        start_seg.addGrip();
      }
      // Remember that this is a starter seg
      start_i = i;
    } else if(next_seg && next_seg.type === 1) {
      // This is the last real segment of a closed sub-path
      // Next is first seg after "M"
      seg.next = segs[start_i+1];
      
      // First seg after "M"'s prev is this
      seg.next.prev = seg;
      seg.mate = segs[start_i];
      seg.addGrip();
      if(this.first_seg === null) {
        this.first_seg = seg;
      }
    } else if(!next_seg) {
      if(seg.type !== 1) {
        // Last seg, doesn't close so add a grip
        // to last sub-path's first point
        var start_seg = segs[start_i];
        start_seg.next = segs[start_i+1];
        start_seg.next.prev = start_seg;
        start_seg.addGrip();
        seg.addGrip();

        if(!this.first_seg) {
          // Open path, so set first as real first and add grip
          this.first_seg = segs[start_i];
        }
      }
    } else if(seg.type !== 1){
      // Regular segment, so add grip and its "next"
      seg.addGrip();
      
      // Don't set its "next" if it's an "M"
      if(next_seg && next_seg.type !== 2) {
        seg.next = next_seg;
        seg.next.prev = seg;
      }
    }
  }
  return this;
};

svgedit.path.Path.prototype.eachSeg = function(fn) {
  var len = this.segs.length
  for(var i=0; i < len; i++) {
    var ret = fn.call(this.segs[i], i);
    if(ret === false) break;
  }
};

svgedit.path.Path.prototype.addSeg = function(index) {
  // Adds a new segment
  var seg = this.segs[index];
  if(!seg.prev) return;

  var prev = seg.prev;
  var newseg;
  switch(seg.item.pathSegType) {
  case 4:
    var new_x = (seg.item.x + prev.item.x) / 2;
    var new_y = (seg.item.y + prev.item.y) / 2;
    newseg = this.elem.createSVGPathSegLinetoAbs(new_x, new_y);
    break;
  case 6: //make it a curved segment to preserve the shape (WRS)
    // http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm#Geometric_interpretation
    var p0_x = (prev.item.x + seg.item.x1)/2;
    var p1_x = (seg.item.x1 + seg.item.x2)/2;
    var p2_x = (seg.item.x2 + seg.item.x)/2;
    var p01_x = (p0_x + p1_x)/2;
    var p12_x = (p1_x + p2_x)/2;
    var new_x = (p01_x + p12_x)/2;
    var p0_y = (prev.item.y + seg.item.y1)/2;
    var p1_y = (seg.item.y1 + seg.item.y2)/2;
    var p2_y = (seg.item.y2 + seg.item.y)/2;
    var p01_y = (p0_y + p1_y)/2;
    var p12_y = (p1_y + p2_y)/2;
    var new_y = (p01_y + p12_y)/2;
    newseg = this.elem.createSVGPathSegCurvetoCubicAbs(new_x,new_y, p0_x,p0_y, p01_x,p01_y);
    var pts = [seg.item.x,seg.item.y,p12_x,p12_y,p2_x,p2_y];
    svgedit.path.replacePathSeg(seg.type,index,pts);
    break;
  }

  svgedit.path.insertItemBefore(this.elem, newseg, index);
};

svgedit.path.Path.prototype.deleteSeg = function(index) {
  var seg = this.segs[index];
  var list = this.elem.pathSegList;
  
  seg.show(false);
  var next = seg.next;
  if(seg.mate) {
    // Make the next point be the "M" point
    var pt = [next.item.x, next.item.y];
    svgedit.path.replacePathSeg(2, next.index, pt);
    
    // Reposition last node
    svgedit.path.replacePathSeg(4, seg.index, pt);
    
    list.removeItem(seg.mate.index);
  } else if(!seg.prev) {
    // First node of open path, make next point the M
    var item = seg.item;
    var pt = [next.item.x, next.item.y];
    svgedit.path.replacePathSeg(2, seg.next.index, pt);
    list.removeItem(index);
    
  } else {
    list.removeItem(index);
  }
};

svgedit.path.Path.prototype.subpathIsClosed = function(index) {
  var closed = false;
  // Check if subpath is already open
  svgedit.path.path.eachSeg(function(i) {
    if(i <= index) return true;
    if(this.type === 2) {
      // Found M first, so open
      return false;
    } else if(this.type === 1) {
      // Found Z first, so closed
      closed = true;
      return false;
    }
  });
  
  return closed;
};

svgedit.path.Path.prototype.removePtFromSelection = function(index) {
  var pos = this.selected_pts.indexOf(index);
  if(pos == -1) {
    return;
  } 
  this.segs[index].select(false);
  this.selected_pts.splice(pos, 1);
};

svgedit.path.Path.prototype.clearSelection = function() {
  this.eachSeg(function(i) {
    // 'this' is the segment here
    this.select(false);
  });
  this.selected_pts = [];
};

svgedit.path.Path.prototype.storeD = function() {
  this.last_d = this.elem.getAttribute('d');
};

svgedit.path.Path.prototype.show = function(y) {
  // Shows this path's segment grips
  this.eachSeg(function() {
    // 'this' is the segment here
    this.show(y);
  });
  if(y) {
    this.selectPt(this.first_seg.index);
  }
  return this;
};

// Move selected points 
svgedit.path.Path.prototype.movePts = function(d_x, d_y) {
  var i = this.selected_pts.length;
  while(i--) {
    var seg = this.segs[this.selected_pts[i]];
    seg.move(d_x, d_y);
  }
};

svgedit.path.Path.prototype.moveCtrl = function(d_x, d_y) {
  var seg = this.segs[this.selected_pts[0]];
  seg.moveCtrl(this.dragctrl, d_x, d_y);
  if(link_control_pts) {
    seg.setLinked(this.dragctrl);
  }
};

svgedit.path.Path.prototype.setSegType = function(new_type) {
  this.storeD();
  var i = this.selected_pts.length;
  var text;
  while(i--) {
    var sel_pt = this.selected_pts[i];
    
    // Selected seg
    var cur = this.segs[sel_pt];
    var prev = cur.prev;
    if(!prev) continue;
    
    if(!new_type) { // double-click, so just toggle
      text = "Toggle Path Segment Type";

      // Toggle segment to curve/straight line
      var old_type = cur.type;
      
      new_type = (old_type == 6) ? 4 : 6;
    } 
    
    new_type = new_type-0;
    
    var cur_x = cur.item.x;
    var cur_y = cur.item.y;
    var prev_x = prev.item.x;
    var prev_y = prev.item.y;
    var points;
    switch ( new_type ) {
    case 6:
      if(cur.olditem) {
        var old = cur.olditem;
        points = [cur_x,cur_y, old.x1,old.y1, old.x2,old.y2];
      } else {
        var diff_x = cur_x - prev_x;
        var diff_y = cur_y - prev_y;
        // get control points from straight line segment
        /*
        var ct1_x = (prev_x + (diff_y/2));
        var ct1_y = (prev_y - (diff_x/2));
        var ct2_x = (cur_x + (diff_y/2));
        var ct2_y = (cur_y - (diff_x/2));
        */
        //create control points on the line to preserve the shape (WRS)
        var ct1_x = (prev_x + (diff_x/3));
        var ct1_y = (prev_y + (diff_y/3));
        var ct2_x = (cur_x - (diff_x/3));
        var ct2_y = (cur_y - (diff_y/3));
        points = [cur_x,cur_y, ct1_x,ct1_y, ct2_x,ct2_y];
      }
      break;
    case 4:
      points = [cur_x,cur_y];
      
      // Store original prevve segment nums
      cur.olditem = cur.item;
      break;
    }
    
    cur.setType(new_type, points);
  }
  svgedit.path.path.endChanges(text);
};

svgedit.path.Path.prototype.selectPt = function(pt, ctrl_num) {
  this.clearSelection();
  if(pt == null) {
    this.eachSeg(function(i) {
      // 'this' is the segment here.
      if(this.prev) {
        pt = i;
      }
    });
  }
  this.addPtsToSelection(pt);
  if(ctrl_num) {
    this.dragctrl = ctrl_num;
    
    if(link_control_pts) {
      this.segs[pt].setLinked(ctrl_num);
    }
  }
};

// Update position of all points
svgedit.path.Path.prototype.update = function() {
  var elem = this.elem;
  if(svgedit.utilities.getRotationAngle(elem)) {
    this.matrix = svgedit.math.getMatrix(elem);
    this.imatrix = this.matrix.inverse();
  } else {
    this.matrix = null;
    this.imatrix = null;
  }

  this.eachSeg(function(i) {
    this.item = elem.pathSegList.getItem(i);
    this.update();
  });

  return this;
};

svgedit.path.getPath_ = function(elem) {
  var p = pathData[elem.id];
  if(!p) p = pathData[elem.id] = new svgedit.path.Path(elem);
  return p;
};

svgedit.path.removePath_ = function(id) {
  if(id in pathData) delete pathData[id];
};

var getRotVals = function(x, y, oldcx, oldcy, newcx, newcy, angle) {
  dx = x - oldcx;
  dy = y - oldcy;
  
  // rotate the point around the old center
  r = Math.sqrt(dx*dx + dy*dy);
  theta = Math.atan2(dy,dx) + angle;
  dx = r * Math.cos(theta) + oldcx;
  dy = r * Math.sin(theta) + oldcy;
  
  // dx,dy should now hold the actual coordinates of each
  // point after being rotated

  // now we want to rotate them around the new center in the reverse direction
  dx -= newcx;
  dy -= newcy;
  
  r = Math.sqrt(dx*dx + dy*dy);
  theta = Math.atan2(dy,dx) - angle;
  return {'x':(r * Math.cos(theta) + newcx)/1,
    'y':(r * Math.sin(theta) + newcy)/1};
};

// If the path was rotated, we must now pay the piper:
// Every path point must be rotated into the rotated coordinate system of 
// its old center, then determine the new center, then rotate it back
// This is because we want the path to remember its rotation

// TODO: This is still using ye olde transform methods, can probably
// be optimized or even taken care of by recalculateDimensions
svgedit.path.recalcRotatedPath = function() {
  var current_path = svgedit.path.path.elem;
  var angle = svgedit.utilities.getRotationAngle(current_path, true);
  if(!angle) return;
//  selectedBBoxes[0] = svgedit.path.path.oldbbox;
  var box = svgedit.utilities.getBBox(current_path),
    oldbox = svgedit.path.path.oldbbox,//selectedBBoxes[0],
    oldcx = oldbox.x + oldbox.width/2,
    oldcy = oldbox.y + oldbox.height/2,
    newcx = box.x + box.width/2,
    newcy = box.y + box.height/2,
    
  // un-rotate the new center to the proper position
    dx = newcx - oldcx,
    dy = newcy - oldcy,
    r = Math.sqrt(dx*dx + dy*dy),
    theta = Math.atan2(dy,dx) + angle;
    
  newcx = r * Math.cos(theta) + oldcx;
  newcy = r * Math.sin(theta) + oldcy;
  
  var list = current_path.pathSegList,
    i = list.numberOfItems;
  while (i) {
    i -= 1;
    var seg = list.getItem(i),
      type = seg.pathSegType;
    if(type == 1) continue;
    
    var rvals = getRotVals(seg.x,seg.y, oldcx, oldcy, newcx, newcy, angle),
      points = [rvals.x, rvals.y];
    if(seg.x1 != null && seg.x2 != null) {
      c_vals1 = getRotVals(seg.x1, seg.y1, oldcx, oldcy, newcx, newcy, angle);
      c_vals2 = getRotVals(seg.x2, seg.y2, oldcx, oldcy, newcx, newcy, angle);
      points.splice(points.length, 0, c_vals1.x , c_vals1.y, c_vals2.x, c_vals2.y);
    }
    svgedit.path.replacePathSeg(type, i, points);
  } // loop for each point

  box = svgedit.utilities.getBBox(current_path);            
//  selectedBBoxes[0].x = box.x; selectedBBoxes[0].y = box.y;
//  selectedBBoxes[0].width = box.width; selectedBBoxes[0].height = box.height;
  
  // now we must set the new transform to be rotated around the new center
  var R_nc = svgroot.createSVGTransform(),
    tlist = svgedit.transformlist.getTransformList(current_path);
  R_nc.setRotate((angle * 180.0 / Math.PI), newcx, newcy);
  tlist.replaceItem(R_nc,0);
};

// ====================================
// Public API starts here

svgedit.path.clearData =  function() {
  pathData = {};
};

})();
